﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters;

namespace Twitter
{
    public class Twitter
    {
        HttpWebRequest _Request;
        string _Username;
        string _Password;
        bool _logged;
        private static Twitter _twitter;

        public event EventHandler<TwitterEventArgs> OnCredentialsVerified;
        public event EventHandler<TwitterEventArgs> OnTweetPosted;
        public event EventHandler<TwitterEventArgs> OnGetTweets;
        
        //making it Singleton
        private Twitter()
        { }

        /// <summary>
        /// Gets a unique instance of the class. Singleton pattern
        /// </summary>
        /// <returns></returns>
        public static Twitter GetInstance()
        {
            if (_twitter == null)
                _twitter = new Twitter();

            return _twitter;
        }

        /// <summary>
        /// Lets us puts our twitter credentials. Raises an exception if login fails
        /// </summary>
        /// <param name="username"></param>
        /// <param name="password"></param>
        public void Login(string username, string password)
        {
            try
            {                
                    _Username = username;
                    _Password = password;
                    _logged = true;
            }
            catch (Exception ex)
            {
                _Username = "";
                _Password = "";
                throw ex;
            }

        }

        /// <summary>
        /// Retunrs the public timeline. No login required. Works as a test method
        /// </summary>
        /// <returns></returns>
        public TweetsCollection GetPublicTweets()
        {
            TweetsCollection tweets = new TweetsCollection();
            //try
            //{

            //    string cad = MakeRequest("http://twitter.com/statuses/public_timeline", new Dictionary<string,string>(), TwitterRequestFormatEnum.xml, TwitterRequestMethodEnum.GET);

            //    XmlDocument doc = new XmlDocument();
            //    doc.LoadXml(cad);
            //    doc.Normalize();
            //    XmlNodeList nodes = doc.SelectNodes("/statuses/status");
            //    foreach (XmlNode node in nodes)
            //    {
            //        try
            //        {

            //            Tweet tweet = Functions.DeserializeObject<Tweet>(node.OuterXml);
            //            tweets.Add(tweet);
            //        }
            //        catch { }
            //    }
                     
            //}
            //catch (Exception ex)
            //{
            //    throw;
            //}
            return tweets;
        }

        /// <summary>
        /// Get all tweets from friends. Need to be loged on 
        /// </summary>
        /// <returns></returns>
        public Tweet[] GetTweets()
        {
            List<Tweet> tweets = new List<Tweet>();
            try
            {
                if (!_logged)
                    throw new TwitterException("User not logged in");
                string cad = MakeRequest("http://twitter.com/statuses/friends_timeline", new Dictionary<string, string>(), TwitterRequestFormatEnum.xml, TwitterRequestMethodEnum.GET);
              
                tweets = new List<Tweet>( Functions.DeserializeCollection(cad) );

            }
            catch 
            {
                throw;
            }
            return tweets.ToArray();
        }

        public void GetTweetsAsync()
        {
            if (!_logged)
                throw new TwitterException("User not logged in");

            MakeRequestAsync("http://twitter.com/statuses/friends_timeline", new Dictionary<string, string>(), TwitterRequestFormatEnum.xml, TwitterRequestMethodEnum.GET, new AsyncCallback(GetTweetsWebResult));
        }

        private void GetTweetsWebResult(IAsyncResult result)
        {
            HttpWebResponse _Response = (HttpWebResponse)_Request.GetResponse();
            TwitterEventArgs e = new TwitterEventArgs();
            if (_Response.StatusCode != HttpStatusCode.OK)
            {
                e.ex = new TwitterException("Status not OK :(");
            }
            else
            {
                Stream Answer = _Response.GetResponseStream();
                StreamReader _Answer = new StreamReader(Answer);

                e.Status = _Response.StatusCode.ToString();
                e.Tweets = new List<Tweet>(Functions.DeserializeCollection(_Answer.ReadToEnd()));
            }

            if (GetTweetsAsyncSingleDelegate != null)
                GetTweetsAsyncSingleDelegate(this, e);
            if (SingleEventCall) return;
            if (OnGetTweets != null)
            {
                foreach (Delegate del in OnGetTweets.GetInvocationList())
                {
                    try
                    {
                        del.DynamicInvoke(new object[] { this, e });
                    }
                    catch
                    { }
                }
            }
        }

        public EventHandler<TwitterEventArgs> GetTweetsAsyncSingleDelegate { get; set; }

        public void GetMyTweets()
        { }

        public Tweet GetTweet(int ID)
        {
            return null;
        }

        /// <summary>
        /// Post a tweet in your timeline
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public Tweet PostTweet(string text)
        {
            Tweet tweet = new Tweet();
            if (!_logged)
                throw new TwitterException("User not logged in");
            if (string.IsNullOrEmpty(text))
                throw new TwitterException("You must write a tweet's text, asshole!");
            text = Functions.HtmlDecode(text);
            Dictionary<string, string> values = new Dictionary<string, string>();
            values.Add("status", text);

            string cad = MakeRequest("http://twitter.com/statuses/update", values, TwitterRequestFormatEnum.xml, TwitterRequestMethodEnum.POST);

            try
            {

                tweet = Functions.DeserializeObject<Tweet>(cad);
            }
            catch (Exception ex) 
            { }
            return tweet;
        }

        public void PostTweetAsync(string text)
        {
            if (!_logged)
                throw new TwitterException("User not logged in");
            if (string.IsNullOrEmpty(text))
                throw new TwitterException("You must write a something!");
            text = Functions.HtmlDecode(text);
            Dictionary<string, string> values = new Dictionary<string, string>();
            values.Add("status", text);

            MakeRequestAsync("http://twitter.com/statuses/update", values, TwitterRequestFormatEnum.xml, TwitterRequestMethodEnum.POST, new AsyncCallback(PostTweetAsyncWebResult));

        }

        private void PostTweetAsyncWebResult(IAsyncResult result)
        {
            HttpWebResponse _Response = (HttpWebResponse)_Request.GetResponse();
            TwitterEventArgs e = new TwitterEventArgs();
            if (_Response.StatusCode != HttpStatusCode.OK)
            {
                e.ex = new TwitterException("Status not OK :(");
                e.Status = _Response.StatusCode.ToString();
            }
            else
            {
                Stream Answer = _Response.GetResponseStream();
                StreamReader _Answer = new StreamReader(Answer);

                e.Status = _Response.StatusCode.ToString();
                e.Tweets = new List<Tweet>(){ Functions.DeserializeObject<Tweet>(_Answer.ReadToEnd()) };
            }

            if (PostTweetAsyncSingleDelegate != null)
                PostTweetAsyncSingleDelegate(this, e);
            if (SingleEventCall) return;

            if (OnTweetPosted != null)
            {
                foreach (Delegate del in OnTweetPosted.GetInvocationList())
                {
                    try
                    {
                        del.DynamicInvoke(new object[] { this, e });
                    }
                    catch
                    { }
                }
            }

        }

        public EventHandler<TwitterEventArgs> PostTweetAsyncSingleDelegate { get; set; }

        public void GetReplies()
        {

        }

        public void DeleteTwitt()
        {

        }

        public void GetFriends()
        { }

        public void GetFollowers()
        { }

        public void GetFriendInfo()
        { }

        public void GetDirectMessages()
        { }

        public void GetSentDirectMessages()
        { }

        public void SendNewDirectMessage(TwitterUser User, string text)
        { }

        public void DestroyDirectMessage(TwitterUser User)
        { }

        public void BeFriendUser(TwitterUser User)
        { }

        public void UnFriendUser(TwitterUser User)
        { }

        public void TestFriendship(TwitterUser User)
        { }

        public bool VerifyCredentials(string username, string password)
        {
            _Username = username;
            _Password = password;
            string cad = MakeRequest("http://twitter.com/account/verify_credentials", new Dictionary<string, string>(), TwitterRequestFormatEnum.xml, TwitterRequestMethodEnum.GET);
            bool res = cad.Contains("true");
            
            return res;
        }

        public void VerifyCredentialsAsync(string username, string password)
        {
            _Username = username;
            _Password = password;
            MakeRequestAsync("http://twitter.com/account/verify_credentials", new Dictionary<string, string>(), TwitterRequestFormatEnum.xml, TwitterRequestMethodEnum.GET, new AsyncCallback(VerifyCredentialsAsyncWebResult));
        }

        private void VerifyCredentialsAsyncWebResult(IAsyncResult result)
        {
            TwitterEventArgs e = new TwitterEventArgs();
            try
            {
                HttpWebResponse _Response = (HttpWebResponse)_Request.GetResponse();
                
                if (_Response.StatusCode != HttpStatusCode.OK)
                {
                    
                }
                else
                {
                    Stream Answer = _Response.GetResponseStream();
                    StreamReader _Answer = new StreamReader(Answer);

                    e.Status = _Response.StatusCode.ToString();
                    //e.Tweets = new List<Tweet>() { Functions.DeserializeObject<Tweet>(_Answer.ReadToEnd()) };
                }
            }
            catch (WebException ex)
            {
                e.ex = new TwitterException("Your credentials doesn't work, dude! :(");
            }

            if (VerifyCredentiaslAsyncSingleDelegate != null)
                VerifyCredentiaslAsyncSingleDelegate(this, e);
            if (SingleEventCall) return;
            if (OnCredentialsVerified != null)
            {
                foreach (Delegate del in OnCredentialsVerified.GetInvocationList())
                {
                    try
                    {
                        del.DynamicInvoke(new object[] { this, e });
                    }
                    catch
                    { }
                }
            }

        }

        public EventHandler<TwitterEventArgs> VerifyCredentiaslAsyncSingleDelegate { get; set; }

        public void Logout()
        { }

        public void UpdateLocation()
        { }

        public void UpdateDeliveryDevice() { }

        public void GetRemainingRequest() { }

        public void GetRecentFavorites() { }

        public void FavoriteUser(TwitterUser User) { }

        public void UnFavoriteUser(TwitterUser User) { }

        public void FollowUser(TwitterUser User) { }

        public void UnFollowUser(TwitterUser User) { }

        public void BlockUser(TwitterUser User) { }

        public void UnBlockUser(TwitterUser User) { }

        public bool TestConnectonToServer()
        {
            bool res;
            try
            {
                string cad = MakeRequest("http://twitter.com/help/test", new Dictionary<string,string>(), TwitterRequestFormatEnum.xml, TwitterRequestMethodEnum.GET);
                res = cad.Contains("true");
            }
            catch (Exception ex)
            {
                throw;
            } 
            return res;
        
        }

        private string MakeRequest(string url, Dictionary<string, string> values, TwitterRequestFormatEnum format, TwitterRequestMethodEnum method)
        {
                       
            byte[] Buffer = MakeBuffer(values);
            _Request = HttpWebRequest.Create(url + "." + format.ToString()) as HttpWebRequest;
            
            CredentialCache mycache = new CredentialCache();
            mycache.Add(new Uri(url), "Basic",
                        new NetworkCredential(_Username, _Password));
            _Request.Method = method.ToString() ;
            _Request.Credentials = mycache;

            _Request.ContentType = "application/x-www-form-urlencoded";

            if (Buffer.Length > 0)
            {
                
                _Request.ContentLength = Buffer.Length;
                Stream PostData = _Request.GetRequestStream();

                PostData.Write(Buffer, 0, Buffer.Length);
                PostData.Close();
            }

            //_Request.BeginGetResponse(new AsyncCallback(AsyncWebResult), new object[]{ _Request, thingToDo}); 

            HttpWebResponse _Response = (HttpWebResponse)_Request.GetResponse();

            if (_Response.StatusCode != HttpStatusCode.OK)
            {
                
            }

            Stream Answer = _Response.GetResponseStream();
            StreamReader _Answer = new StreamReader(Answer);

            return _Answer.ReadToEnd();
        }

        private void MakeRequestAsync(string url, Dictionary<string, string> values, TwitterRequestFormatEnum format, TwitterRequestMethodEnum method, AsyncCallback methodToCall)
        {
            byte[] Buffer = MakeBuffer(values);
            _Request = HttpWebRequest.Create(url + "." + format.ToString()) as HttpWebRequest;

            CredentialCache mycache = new CredentialCache();
            mycache.Add(new Uri(url), "Basic",
                        new NetworkCredential(_Username, _Password));
            _Request.Method = method.ToString();
            _Request.Credentials = mycache;
            _Request.ServicePoint.Expect100Continue = false;

            _Request.ContentType = "application/x-www-form-urlencoded";

            if (Buffer.Length > 0)
            {

                _Request.ContentLength = Buffer.Length;
                Stream PostData = _Request.GetRequestStream();

                PostData.Write(Buffer, 0, Buffer.Length);
                PostData.Close();
            }

            _Request.BeginGetResponse(methodToCall, null);

        }

        private byte[] MakeBuffer(Dictionary<string, string> values)
        {
            byte[] buffer;
            string strBuffer = "";
            List<string> vars = new List<string>();
            try
            {
                if (values.Count > 0)
                {
                    foreach (KeyValuePair<string, string> kvp in values)
                        vars.Add(kvp.Key + "=" + kvp.Value);

                    strBuffer = String.Join("&", vars.ToArray());
                }
                buffer = Encoding.ASCII.GetBytes(strBuffer);
                //Initialisation


            }
            catch (Exception ex)
            {

                throw;
            }
            return buffer;
        }



        public bool CachedTweets { get; set; }

        public bool SingleEventCall { get; set; }
    }
}
